package com.flute.icrawler.app.service;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.CookieStore;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.params.HttpClientParams;
import org.apache.http.client.protocol.ClientContext;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.flute.icrawler.app.entity.CookieBean;
import com.flute.icrawler.policy.bdb.storage.BdbStorageUtil;
import com.flute.icrawler.policy.bdb.storage.IStorage;

/**
 * 提供COOKIE信息的存储服务
 * 提供学习机制，完善同类网站的COOKIE格式信息
 * @author danly
 *
 */
public class CookieService {
	
	private static final String HEAD_NAME_SET_COOKIE = "Set-Cookie";
	private CookieStore cookieStore;
	
	private static final Logger LOGGER = LoggerFactory
	.getLogger(CookieService.class);
	
	private static IStorage mystorage = BdbStorageUtil.getStorage("CookieDatabase");
	
	
	/**
	 * 传入URL，服务将会根据地址进行链接，取COOKIE信息,服务以URL为KEY自动进行数据库存取策略
	 * @param url
	 * @return
	 */
	public static String getCookie(String url)
	{
		//如果不存在键值直接返回空字符串
		
		CookieService cokSev =  new CookieService();
		String cookiestr = cokSev.getCookieFromDataStr(url); 
		cokSev = null;
		return cookiestr;
	}
	/**
	 * 传入URL,链接响应后的response，服务将会根据HEAD信息取COOKIE信息，服务以URL为KEY自动进行数据库存取策略
	 * @param url
	 * @param response
	 * @return
	 */
	private static String getCookie(String url,HttpResponse response)
	{
		    CookieService cokSev =  new CookieService();
			String cookiestr = cokSev.resolvingCookie(url,response);
			cokSev = null;
			return cookiestr;
	}
	/**
	 * 存储COOKIE
	 * @param key
	 * @param cookieval
	 * @return
	 */
	public static boolean registerCookie(String key,String cookieval)
	{
		CookieService cokSev =  new CookieService();
		boolean isOk = cokSev.registerCookieSvr(key, cookieval);
		cokSev = null;
		return isOk;
	}
	/**
	 * 储存逻辑  1.获取URL的HOST
	 *          2.以HOST为键值，取数据库的COOKIE格式（cokFdata），此格式是该类网站的最全最新COOKIE
	 *          3.取并集，同时更新拥有相同键的COOKIE值 如COOKIE1:A=1;C=4  COOKIE2:A=2;B=3 结果为 A=2;C=3;B=4; 完成学习功能
	 *          4.以HOST，URL为键同时存储合并后的COOKIE值
	 * @param key
	 * @param cookieval
	 * @return
	 */
	private boolean registerCookieSvr(String key,String cookieval)
	{
		boolean isupdate = false;
		if(key==null)
		{
			return isupdate;
		}
		URL url = null;
		try {
			url = new URL(key);
		} catch (MalformedURLException e) {
			e.printStackTrace();
		}
		String strHost = url.getHost();
		if(!strHost.startsWith("http"))
		{
			strHost ="http://"+strHost;
		}
		//取数据的COOKIE
		String cokFdata = getCookieFromDataStr(strHost);
		//学习功能 返回当前最全的COOKIE
		String normalizedform  = this.cookieStudy(cookieval, cokFdata);
		isupdate = this.saveCookie(key,normalizedform);
		isupdate = this.saveCookie(strHost,normalizedform);
		return isupdate;
		
		
	}
	private boolean registerCookieSvr(String key,String cookieval,String cokFdata)
	{
		boolean isupdate = false;
		if(key==null)
		{
			return isupdate;
		}
		if(cokFdata.equals(""))
		{
			this.saveCookie(key,cookieval);
		}else
		{
			isupdate = this.isUpateCookie(cookieval, cokFdata);
		}	
		if(isupdate)
		{
			this.saveCookie(key,cookieval);
		}
		return isupdate;
	}  

	/**
	 * 获取response （本类的测试工具）
	 * @param url
	 * @return
	 */
	public HttpResponse httphandle(String url)
	{
		HttpResponse response = null;
		LOGGER.info(" CookieService.httphandle(String url)  "+" url = "+url);
		try {
			HttpClient httpclient = new DefaultHttpClient(this.configureHttpParams());
			//CookieStore cookieStore = new BasicCookieStore();
			// Create a local instance of cookie store
			cookieStore = new BasicCookieStore();
			// Create local HTTP context
			HttpContext localContext = new BasicHttpContext();
			// Bind custom cookie store to the local context
			localContext.setAttribute(ClientContext.COOKIE_STORE, cookieStore);
			HttpGet httpget = new HttpGet(url);
			String testcok = "viewed=\"3838146_1417413_4887438\"; path=/; domain=.douban.com; expires=Wed, 01-Jan-2012 00:00:00 GMT;;SSOLoginState=deleted;MY_YUEKU=c0cbaecd017cdaa27280b8121a39e87f;bid=\"Lu9kPcEm97E\";";
			//String testcok = "bid=\"W2bcVnJFMo\";viewed=\"1394742_4604267_1762969_1427042\";ASP.NET_SessionId=cbvx1i55otyagw45wxktfo45; path=/; HttpOnly;";
			//httpget.addHeader("Cookie", testcok);
			response = httpclient.execute(httpget, localContext);
			
			int iStatusCode = response.getStatusLine().getStatusCode();
			//List<Cookie> cookies = cookieStore.getCookies();
			LOGGER.info(" CookieService.httphandle  iStatusCode "+iStatusCode);
		} catch (ClientProtocolException e) {
			LOGGER.error(" CookieService.httphandle(String url) error "+" url = "+url+e);
			//e.printStackTrace();
		} catch (IOException e) {
			LOGGER.error(" CookieService.httphandle(String url)  error "+" url = "+url+e);
			//e.printStackTrace();
		}
		return  response;
	}
	
	private String resolvingCookie(String strurl,HttpResponse response)
	{
		String cookiestr = null;
		if(response!=null)
		{
			Header cookieHeader = response.getFirstHeader(HEAD_NAME_SET_COOKIE);
			/*Header cookieHeader[] = response.getHeaders("Set-Cookie");
			for(int i=0;i<cookieHeader.length;i++)
			{
				System.out.println("douban headers = "+cookieHeader[i].getValue());
			}*/
			if(cookieHeader!=null){
				cookiestr = cookieHeader.getValue();
			}
			cookiestr = this.resolingCookieSerivce(strurl, cookiestr,null);
		}
		return cookiestr;
	}
	/**
	 * 获取该URL的COOKIE信息
	 * COOKIE格式 NAME=VALUE; 如果子链接没有COOKIE信息 返回HOST的COOKIE信息
	 * 对于需要建立链接访问的地址，先判断本地是否存在，如果不存在再进行RESPHONES链接
	 * @param url
	 */
	private String resolvingCookie(String strurl)
	{
		String cookiestr = null;
		String cookiestrdata = getCookieFromDataStr(strurl);
		if(!cookiestrdata.equals(""))
		{
			return cookiestrdata;
		}

		HttpResponse response = httphandle(strurl);
		if(response == null){
			return "";
		}
		//头信息，Set-Cookie 取COOKIE
		Header cookieHeader = response.getFirstHeader(HEAD_NAME_SET_COOKIE);

		//字符串COOKIE方式保存
		if(cookieHeader!=null)
		{
			cookiestr = cookieHeader.getValue();
		}
		cookiestr = this.resolingCookieSerivce(strurl, cookiestr,cookiestrdata);
		System.out.println("  resolvingCookie(String strurl) return = " +cookiestr + " url = "+strurl);
		return cookiestr;
		
	}
	/**
	 * 从数据库中取COOKIE字符串:1.以传入的KEY取
	 *                       2.以传入KEY的HOST取
	 * 没有取到返回空字符串 
	 * @param key
	 * @return
	 */
	private String getCookieFromDataStr(String key)
	{
		String cookiestr = "";
		StringBuffer cookievalue = new StringBuffer(2000);
		try{
			
			//当前修改为一类网站只维护一个COOKIE格式，不存在子网页维护自己的COOKIE的情况
			//cookievalue =  (StringBuffer)mystorage.search(key);
			
			//if(cookievalue == null)
			//{
				URL url = null;
				try {
					url = new URL(key);
				} catch (MalformedURLException e) {
					e.printStackTrace();
				}
				String strHost = url.getHost();
				if(!strHost.startsWith("http"))
				{
					strHost ="http://"+strHost;
				}
				cookievalue =  (StringBuffer)mystorage.search(strHost);
			//}
			
			if(cookievalue!=null){
				cookiestr =  cookievalue.toString();
			}else{
				return "";
			}
			

		}catch(Exception e)
		{
			LOGGER.error(" CookieService.getCookieFromDataStr(String key) error "+" key = "+key+e);
		}
		return cookiestr;
	}
	/**
	 * COOkiE对象形式保存
	 * @param cookie
	 * @param strUrl
	 */
	private boolean saveCookie(Cookie cookie,String key)
	{
		try {
				
			    CookieBean cookiebean = new CookieBean();
	           // 序列化 COOKIE对象(转化方式)
				cookiebean.setCookie(cookie);
		} catch (Exception e) {
			e.printStackTrace();
			LOGGER.error(" CookieService.saveCookie(Cookie cookie,String key) error "+" key = "+key+e);
		}
		return true;
	}
	/**
	 * 字符串COOKIE保存
	 * @param strcookie
	 * @param strUrl
	 */
	private boolean saveCookie(String key,String strcookie)
	{
		try {

			mystorage.put(key, new StringBuffer(strcookie));
			mystorage.sync();
		} catch (Exception e) {
			e.printStackTrace();
			LOGGER.error(" CookieService.saveCookie(String strcookie,String key) error "+" key = "+key+e);
		}
		return true;
	}
	public static void main(String args[])
	{
CookieService.registerCookie("http://music.douban.com", "bid=\"dfdfd\"");
		
		System.out.println("getCookie:"+CookieService.getCookie("http://music.douban.com"));
		// http://music.douban.com/subject/1452226/ 这个地址需要注意 具备2个COOKIE 统一域环境内
		//CookieService a = new CookieService();
		//a.getCookie("http://music.douban.com");
		//String cookiestr = a.getCookie("http://ditu.google.cn/");
		/*registerCookie("http://music.douban.com/review/3349975/","123");
		String cookiestr  = getCookie("http://music.douban.com/review/3349975/");
		LOGGER.info(" CookieService.main  "+" getCookie = "+cookiestr);
		
		
		
		registerCookie("http://music.douban.com/review/3349975/","456");
		 cookiestr  = getCookie("http://music.douban.com/review/3349975/");
		LOGGER.info(" CookieService.main  "+" getCookie = "+cookiestr);*/
		CookieService COKSVR = new CookieService();
		COKSVR.getCookie("http://12323");
		//COKSVR.registerCookieSvr("http://music.douban.com", "bid=\"haha\"");
		//registerCookie("http://music.douban.com/review/3349975/","321");
		//String cookiestr = getCookie("http://music.douban.com");
		String cookiestr  = getCookie("http://music.douban.com/subject/4308254/",COKSVR.httphandle("http://music.douban.com/subject/4308254/"));
		//String cookiestr_douban  = getCookie("http://douban.music.com",COKSVR.httphandle("http://douban.music.com"));
		//LOGGER.info(" CookieService.main  "+" getCookie_google = "+cookiestr);
		//LOGGER.info(" CookieService.main  "+" cookiestr_douban = "+cookiestr_douban);
		
		
		String cookiestr3  ="A=3";
		String cookiestr2  ="A=1;B=2;C=3;D=4;E=5;ok";
		String test = COKSVR.cookieStudy(cookiestr3,cookiestr2);
		//CookieService
		
		//LOGGER.info(" CookieService.main  "+" test = "+test);
		//a.readDatebyKey("http://music.douban.com");
	}
	
	/**
	 * 组装基础参数
	 * @return
	 * @throws RuntimeException
	 */
	private HttpParams configureHttpParams() throws RuntimeException {
		String userAgent = "Mozilla/5.0 (compatible; heritrix/1.14.3 +http://www.baidu.com)";
		userAgent = "HttpComponents/1.1";
		HttpParams params = new BasicHttpParams();
		HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
		HttpProtocolParams.setContentCharset(params, "UTF-8");
		HttpProtocolParams.setUserAgent(params, userAgent);
		HttpConnectionParams.setConnectionTimeout(params, new Integer(32000));
		HttpConnectionParams.setSoTimeout(params, new Integer(12000));
		HttpClientParams.setRedirecting(params, true);
		return params;
	}
	/**
	 * 比较COOKIE字符串，如果数据库内的字符串与取来的相同则不需要更新，否则更新
	 * 比较规则，长度相同的为相同，COOKIE值第一位（name）相同的为相同
	 * @param cookiestr
	 */
	private boolean isUpateCookie(String cookiestr,String cookiestrdbd)
	{
		if(cookiestr!=null){
			String[] cookiestrs  =  cookiestr.split("//;");
			String[] cookiestrdbds  =  cookiestrdbd.split("//;");
			//长度相同 不用再比较
			if(cookiestrs.length>cookiestrdbds.length){
				//需要更新
				return true;
			}else{
					String firstflag_a =  cookiestrs[0];
					String firstflag_b =  cookiestrdbds[0];
					String firstflag_a_name[] = firstflag_a.split("//=");
					String firstflag_b_name[] = firstflag_b.split("//=");
					if(!firstflag_a_name[0].equals(firstflag_b_name[0])){
						//需要更新
						return true;
				 }
			}
		}
		return false;
	}
	/**
	 * COOKIE存储逻辑
	 * @param key  URL键值
	 * @param cookiestr  传入的COOKIE
	 * @param cookiestrdata   数据库取得的COOKIE
	 * @return
	 */
	private  String  resolingCookieSerivce(String key,String cookiestr,String cookiestrdata)
	{
		if(cookiestrdata == null)
		{
			cookiestrdata = getCookieFromDataStr(key);
		}
		if(StringUtils.isNotEmpty(cookiestr)&&!cookiestr.equals(cookiestrdata))
		{
			this.registerCookieSvr(key, cookiestr, cookiestrdata);
		}else
		{
			//取数据库内的COOKIE
		    
		    if(!cookiestrdata.equals("")){
		    	cookiestr = cookiestrdata;
		    }else
		    {
		    	URL url = null;
				try {
					url = new URL(key);
				} catch (MalformedURLException e) {
					e.printStackTrace();
				}
				String strHost = url.getHost();
				if(!strHost.startsWith("http"))
				{
					strHost ="http://"+strHost;
				}
				return resolvingCookie(strHost);
		    }
			
		}
		  LOGGER.info(" CookieService.resolvingCookie(String strurl)  debug "+" cookiestr = "+cookiestr + " cookiestrdata =  " + cookiestrdata);
		
		return cookiestr;
	}
	/**
	 * 根据COOKIE格式 KEY=VAL； 取并集
	 * @param cookiestr的VAL将会在并集中保存
	 * @param cookstrFdata
	 * @return
	 */
	private String  cookieStudy(String cookiestr,String cookstrFdata)
	{
		List<String> list = new ArrayList<String>();
		HashMap<String, String> map = new HashMap<String, String>();
		String newcookiestr = "";
		
		if(cookstrFdata!=null&&cookstrFdata.equals(""))
		{
			return cookiestr;
		}else{
			//进行合并
			if(cookiestr ==null){
				return cookiestr;
			}
			if(cookiestr.endsWith(";")){
				cookiestr = cookiestr + cookstrFdata;
			}else{
				cookiestr = cookiestr +";" + cookstrFdata;
			}
			
			String cookiestrs[] = cookiestr.split(";");
			for(int i=0;i<cookiestrs.length;i++)
			{		
				String cookiestrsothers[] = cookiestrs[i].split("=");
				for(int j=0;j<cookiestrsothers.length;j++)
				{
					if(!map.containsKey(cookiestrsothers[0]))
					{
						list.add(cookiestrsothers[0]);
						if(cookiestrsothers.length==2){
							map.put(cookiestrsothers[0], cookiestrsothers[1]);
					    //VALUE里面有“=”号的情况
						}else if(cookiestrsothers.length>2)
						{
							String temp ="";
							for(int k=1;k<cookiestrsothers.length;k++){
								temp =temp +"="+cookiestrsothers[k];
							}
							map.put(cookiestrsothers[0], temp.substring(1));
						}
						else{
							map.put(cookiestrsothers[0], "");
						}
						
					}
				}
			}
		}
		if(list.size()>0){
			for(int i=0;i<list.size();i++){
				String newcookiestrnametemp =(String)list.get(i);
				String newcookiestrvaluetemp = (String)map.get(newcookiestrnametemp);
				if(!newcookiestrvaluetemp.equals("")){
					newcookiestr += newcookiestrnametemp + "=" + newcookiestrvaluetemp +";";
				}else{
					newcookiestr += newcookiestrnametemp +";";
				}
				
			}
		}
		return newcookiestr;
	}
	
	

}
